package cn.demoncat.util.lang;

import cn.demoncat.util.exception.ParamRuntimeException;
import cn.demoncat.util.lang.constant.DateConstant;
import cn.demoncat.util.lang.constant.StringConstant;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;


/**
 * JDK8时间 的处理工具
 * 
 * @author 延晓磊
 *
 * @since 2017年11月13日
 */
public final class LocalDateUtil {

	private static final Logger LOG = LoggerFactory.getLogger(LocalDateUtil.class);

	private static final String ERROR_ILLEGAL = "错误的日期参数【{}】";

	/**
	 * 设置时区
	 *
	 * @param zoneId	中国 Asia/Shanghai，纽约 America/New_York，伦敦 Europe/London
	 */
	public static void setTimeZone(String zoneId){
		TimeZone.setDefault(TimeZone.getTimeZone(zoneId));
	}

	/**
	 * 获取瞬时实例(代表某个的时间，可以精确到纳秒)
	 * 
	 * @param dateTime
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static Instant getInstant(LocalDateTime dateTime){
		if (dateTime == null) {
			return null;
		}
		// LocalDateTime的系统时区的实例
		return dateTime.atZone(DateConstant.ZONE_ID).toInstant();
	}
	 
	/**
	 * 获取瞬时实例
	 * 
	 * @param timestamp	时间戳
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static Instant getInstant(long timestamp){
		if (timestamp == 0) {
			return null;
		}
		return Instant.ofEpochMilli(timestamp);
	}
	
	/**
	 * 获取瞬时实例
	 * 
	 * @param date 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static Instant getInstant(Date date){
		if (date == null) {
			return null;
		}
		return date.toInstant();
	}
	
	/**
	 * 获取瞬时实例(日起始)
	 * 
	 * @param date
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static Instant getInstant(LocalDate date){
		if (date == null) {
			return null;
		}
		return date.atStartOfDay().atZone(DateConstant.ZONE_ID).toInstant();
	}
	
	/**
	 * 获取时间之差 
	 * 
	 * 注：可以通过 Duration的toDays()等获取“天、时、秒”之差
	 * 
	 * @param startTime
	 * @param endTime
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年11月13日
	 */
	public static Duration between(LocalDateTime startTime, LocalDateTime endTime){
		return Duration.between(startTime,endTime);
	}

	/**
	 * 获取时间之差
	 *
	 * 注：可以通过 Duration的toDays()等获取“天、时、秒”之差
	 *
	 * @param startTime
	 * @param endTime
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年11月13日
	 */
	public static Duration between(LocalDate startTime, LocalDate endTime){
		return Duration.between(startTime.atStartOfDay(),endTime.atStartOfDay());
	}
	
	/**
	 * 获取时间之差 
	 * 
	 * 注：可以通过 Duration的toDays()等获取“时、秒”之差
	 * 
	 * @param startTime
	 * @param endTime
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年11月13日
	 */
	public static Duration between(LocalTime startTime, LocalTime endTime){
		return Duration.between(startTime,endTime);
	}

	/**
	 * 获取相差的“年+月+天”
	 *
	 * @param startTime
	 * @param endTime
	 * @return {年，月，日}
	 */
	public static Period betweenDate(LocalDate startTime, LocalDate endTime){
		return Period.between(startTime, endTime);
	}

	/**
	 * 获取相差的“天+时+分+秒”
	 *
	 * @param startTime
	 * @param endTime
	 * @return [天，时，分，秒]
	 */
	public static List<Integer> betweenTime(LocalDateTime startTime, LocalDateTime endTime){
		List<Integer> list = new ArrayList<>(4);
		Duration between = between(startTime, endTime);
		// 天
		list.add((int) between.toDays());
		// 时
		list.add((int) (between.toHours() - list.get(0) * DateConstant.HOURS_OF_DAY));
		// 分
		list.add((int) (between.toMinutes() - list.get(0) * DateConstant.MINUTES_OF_DAY - list.get(1) * DateConstant.MINUTES_OF_HOUR));
		// 秒
		list.add((int) (between.getSeconds() % DateConstant.SECONDS_OF_MINUTE));
		return list;
	}

	/**
	 * 获取相差的年
	 *
	 * @param startTime
	 * @param endTime
	 * @return
	 */
	public static int betweenYear(LocalDate startTime, LocalDate endTime){
		return (int) ChronoUnit.YEARS.between(startTime, endTime);
	}

	/**
	 * 获取相差的月
	 *
	 * @param startTime
	 * @param endTime
	 * @return
	 */
	public static int betweenMonth(LocalDate startTime, LocalDate endTime){
		return (int) ChronoUnit.MONTHS.between(startTime, endTime);
	}

	/**
	 * 获取相差的天
	 *
	 * @param startTime
	 * @param endTime
	 * @return
	 */
	public static int betweenDay(LocalDate startTime, LocalDate endTime){
		return (int) ChronoUnit.DAYS.between(startTime, endTime);
	}

	/**
	 * 补全Date日期
	 * 
	 * @param date	
	 * @return yyyy-MM-dd
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年7月13日
	 */
	public static String fullDate(String date) {
		// 非空校验
		if (StringUtil.isBlank(date)) {
			return date;
		}
		// 长度判断
		int length = date.length();
		if (length == 10) {
			// 规范
			return date;
		}else if (length > 10) {
			// 超长
			return date.substring(0, 10);
		}else {
			// 补全
			if (length == 7) {
				// 月
				return date + "-01";
			}else if (length == 4) {
				// 年
				return date + "-01-01";
			}else {
				LOG.warn(ERROR_ILLEGAL, date);
				throw ParamRuntimeException.ILLEGAL; 
			}
		}
	}

	/**
	 * 补全DateTime日期
	 * 
	 * @param date	
	 * @return yyyy-MM-dd HH:mm:ss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年7月13日
	 */
	public static String fullDateTime(String date) {
		// 非空校验
		if (StringUtil.isBlank(date)) {
			return date;
		}
		// 去除T
		StringUtils.replace(date, "T", StringConstant.SPACING);
		// 长度判断
		int length = date.length();
		if (length == 19) {
			// 规范
			return date;
		}else if (length > 19) {
			// 超长
			return date.substring(0, 19);
		}else {
			// 补全
			if (length == 10) {
				// 日
				return date + " 00:00:00";
			}else if (length == 7) {
				// 月
				return date + "-01 00:00:00";
			}else if (length == 4) {
				// 年
				return date + "-01-01 00:00:00";
			}else if (length == 13) {
				// 时
				return date + ":00:00";
			}else if (length == 16) {
				// 分
				return date + ":00";
			}else {
				LOG.warn(ERROR_ILLEGAL, date);
				throw ParamRuntimeException.ILLEGAL; 
			}
		}
	}

	/**
	 * 字符串转换为日期
	 * 
	 * @param date 	 	日期
	 * @param format 	格式 DateTimeFormatter
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static LocalDateTime toLocalDateTime(String date, DateTimeFormatter format){
		if (StringUtil.isBlank(date)) {
			return null;
		}
		return LocalDateTime.parse(date,format);
	}
	
	/**
	 * 字符串转换为日期
	 * 
	 * @param date 	格式：yyyy-MM-dd HH:mm:ss，yyyy-MM-dd
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static LocalDateTime toLocalDateTime(String date){
		return toLocalDateTime(fullDateTime(date),DateConstant.DATE_TIME_FORMAT);
	}
	
	/**
	 * 字符串转换为日期
	 * 
	 * @param date	格式：yyyyMMddHHmmss
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static LocalDateTime toLocalDateTimeNum(String date){
		return toLocalDateTime(date, DateConstant.DATE_TIME_FORMAT_NUM);
	}
	
	/**
	 * 字符串转换为日期
	 * 
	 * @param date 	 	日期
	 * @param format 	格式 DateTimeFormatter
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static LocalDate toLocalDate(String date, DateTimeFormatter format){
		if (StringUtil.isBlank(date)) {
			return null;
		}
		return LocalDate.parse(date,format);
	}
	
	/**
	 * 字符串转换为日期
	 * 
	 * @param date 	格式：yyyy-MM-dd，yyyy-MM-dd HH:mm:ss
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static LocalDate toLocalDate(String date){
		return toLocalDate(fullDate(date),DateConstant.DATE_FROMAT);
	}
	
	/**
	 * 字符串转换为日期
	 * 
	 * @param date	格式：yyyyMMdd
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static LocalDate toLocalDateNum(String date){
		return toLocalDate(date, DateConstant.DATE_FORMAT_NUM);
	}


	/**
	 * 格式化日期
	 *
	 * @param date
	 * @param formatter
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2021年09月22日
	 */
	public static String format(LocalDateTime date, DateTimeFormatter formatter){
		if (date == null) {
			return StringConstant.EMPTY;
		}
		return date.format(formatter);
	}

	/**
	 * 格式化日期
	 *
	 * @param date
	 * @param formatter
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2021年09月22日
	 */
	public static String format(LocalDate date, DateTimeFormatter formatter){
		if (date == null) {
			return StringConstant.EMPTY;
		}
		return date.format(formatter);
	}

	/**
	 * 格式化日期
	 *
	 * @param date
	 * @param formatter
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2021年09月22日
	 */
	public static String format(LocalTime date, DateTimeFormatter formatter){
		if (date == null) {
			return StringConstant.EMPTY;
		}
		return date.format(formatter);
	}

	/**
	 * 日期转字符串
	 * 
	 * @param date	
	 * @return	格式：yyyy-MM-dd HH:mm:ss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toStr(LocalDateTime date){
		return format(date, DateConstant.DATE_TIME_FORMAT);
	}
	
	/**
	 * 日期转字符串
	 * 
	 * @param date	
	 * @return	格式：yyyy-MM-dd'T'HH:mm:ss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toTStr(LocalDateTime date){
		return format(date, DateConstant.DATE_T_TIME_FORMAT);
	}
	
	/**
	 * 日期转字符串
	 * 
	 * @param date
	 * @return	格式：yyyy-MM-dd
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toStr(LocalDate date){
		return format(date, DateConstant.DATE_FROMAT);
	}
	
	/**
	 * 日期转字符串
	 * 
	 * @param date
	 * @return	格式：HH:mm:ss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toStr(LocalTime date){
		return format(date, DateConstant.TIME_FORMAT);
	}
	
	/**
	 * 当前时间转字符串
	 * 
	 * @return	格式：yyyy-MM-dd HH:mm:ss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toStr(){
		return toStr(LocalDateTime.now());
	}
	
	/**
	 * 当前时间转字符串
	 * 
	 * @return	格式：yyyy-MM-dd'T'HH:mm:ss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toTStr(){
		return toTStr(LocalDateTime.now());
	}
	
	/**
	 * 当前时间转字符串
	 * 
	 * @return	格式：yyyy-MM-dd
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toDateStr(){
		return toStr(LocalDate.now());
	}
	
	/**
	 * 当前时间转字符串
	 * 
	 * @return	格式：HH:mm:ss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toTimeStr(){
		return toStr(LocalTime.now());
	}
	
	/**
	 * 日期转字符串
	 * 
	 * @param date
	 * @return	格式：yyyyMMddHHmmss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toNum(LocalDateTime date){
		return format(date, DateConstant.DATE_TIME_FORMAT_NUM);
	}
	
	/**
	 * 日期转字符串
	 * 
	 * @param date
	 * @return	格式：yyyyMMdd
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toNum(LocalDate date){
		return format(date, DateConstant.DATE_FORMAT_NUM);
	}

	/**
	 * 日期转字符串
	 * 
	 * @param date
	 * @return	格式：HHmmss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toNum(LocalTime date){
		return format(date, DateConstant.TIME_FORMAT_NUM);
	}
	
	/**
	 * 当前时间转字符串
	 * 
	 * @return	格式：yyyyMMddHHmmss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toNum(){
		return toNum(LocalDateTime.now());
	}
	
	/**
	 * 当前时间转字符串
	 * 
	 * @return	格式：yyyyMMdd
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toDateNum(){
		return toNum(LocalDate.now());
	}
	
	/**
	 * 当前时间转字符串
	 * 
	 * @return	格式：HHmmss
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static String toTimeNum(){
		return toNum(LocalTime.now());
	}
	
	/**
	 * 获取当前时间戳
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static long getTime(){
		return System.currentTimeMillis();
	}
	
	/**
	 * 获取时间戳
	 * 
	 * @param dateTime
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static Long getTime(LocalDateTime dateTime){
		if (dateTime == null) {
			return null;
		}
		return getInstant(dateTime).toEpochMilli();
	}
	
	/**
	 * 获取UTC时间戳
	 * 
	 * @param dateTime
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static Long getUtcTime(LocalDateTime dateTime){
		if (dateTime == null) {
			return null;
		}
		return dateTime.toInstant(ZoneOffset.UTC).toEpochMilli();
	}
	
	/**
	 * 获取时间戳
	 * 
	 * @param date	当天0点
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static Long getTime(LocalDate date){
		if (date == null) {
			return null;
		}
		return getTime(date.atStartOfDay());
	}
	
	/**
	 * 获取UTC时间戳
	 * 
	 * @param date	当天0点
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static Long getUtcTime(LocalDate date){
		if (date == null) {
			return null;
		}
		return getUtcTime(date.atStartOfDay());
	}
	
	/**
	 * 获取时间戳
	 * 
	 * @param date	当天0点
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static Long getTime(String date){
		if (StringUtils.isBlank(date)) {
			return null;
		}
		// 包含 : 即日期时间，否则为日期
		if (date.contains(StringConstant.COLON)) {
			return getTime(toLocalDateTime(date));
		}else {
			return getTime(toLocalDate(date));
		}
	}
	
	/**
	 * 获取UTC时间戳
	 * 
	 * @param date	当天0点
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年10月13日
	 */
	public static Long getUtcTime(String date){
		if (StringUtils.isBlank(date)) {
			return null;
		}
		// 包含 : 即日期时间，否则为日期
		if (date.contains(StringConstant.COLON)) {
			return getUtcTime(toLocalDateTime(date));
		}else {
			return getUtcTime(toLocalDate(date));
		}
	}
	
	/**
	 * LocalDateTime 转换 Date
	 * 
	 * @param dateTime
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月14日
	 */
	public static Date toDate(LocalDateTime dateTime){
		if (dateTime == null) {
			return null;
		}
		return Date.from(getInstant(dateTime));
	}
	/**
	 * LocalDate转换 Date
	 * 
	 * @param date
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月14日
	 */
	public static Date toDate(LocalDate date){
		if (date == null) {
			return null;
		}
		return Date.from(getInstant(date));
	}
	
	/**
	 * Date 转换 LocalDateTime
	 * 
	 * @param date 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月14日
	 */
	public static LocalDateTime toLocalDateTime(Date date){
		if (date == null) {
			return null;
		}
		return LocalDateTime.ofInstant(getInstant(date),DateConstant.ZONE_ID);
	}
	
	/**
	 * 时间戳 转换 LocalDateTime
	 * 
	 * @param timestamp 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月14日
	 */
	public static LocalDateTime toLocalDateTime(Long timestamp){
		if (timestamp == null || timestamp == 0) {
			return null;
		}
		return LocalDateTime.ofInstant(getInstant(timestamp),DateConstant.ZONE_ID);
	}
	
	/**
	 * Date 转换 LocalDate
	 * 
	 * @param date 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月14日
	 */
	public static LocalDate toLocalDate(Date date){
		if (date == null) {
			return null;
		}
		return toLocalDateTime(date).toLocalDate();
	}
	
	/**
	 * 时间戳 转换 LocalDate
	 * 
	 * @param timestamp 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月14日
	 */
	public static LocalDate toLocalDate(Long timestamp){
		if (timestamp == null || timestamp == 0) {
			return null;
		}
		LocalDateTime localDateTime = toLocalDateTime(timestamp);
		if (localDateTime != null) {
			return localDateTime.toLocalDate();
		}
		return null;
	}
	
	/**
	 * 字符串转换为日期
	 * 
	 * @param date 	格式：HH:mm:ss
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月16日
	 */
	public static LocalTime toLocalTime(String date){
		if (StringUtils.isBlank(date)) {
			return null;
		}
		// HH:mm:ss
		return LocalTime.parse(date);
	}
	
	/**
	 * Date 转换 LocalTime
	 * 
	 * @param date 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月14日
	 */
	public static LocalTime toLocalTime(Date date){
		if (date == null) {
			return null;
		}
		return toLocalDateTime(date).toLocalTime();
	}
	
	/**
	 * 时间戳 转换 LocalTime
	 * 
	 * @param timestamp 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月14日
	 */
	public static LocalTime toLocalTime(Long timestamp){
		if (timestamp == null || timestamp == 0) {
			return null;
		}
		LocalDateTime localDateTime = toLocalDateTime(timestamp);
		if (localDateTime != null) {
			return localDateTime.toLocalTime();
		}
		return null;
	}
	
	/**
	 * 功能描述：获取明天凌晨0点的时间
	 *
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年6月7日
	 */
	public static LocalDateTime tomorrow0(){
		return LocalDate.now().plusDays(1).atStartOfDay();
	}
	
	/**
	 * 功能描述：获取明天凌晨0点的时间戳
	 *
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年6月7日
	 */
	public static long tomorrow0Num(){
		return getTime(tomorrow0());
	}
	
	/**
	 * 功能描述：距离明天凌晨0点的毫秒数
	 *
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年6月7日
	 */
	public static long tomorrow0Exp() {
		return tomorrow0Num() - System.currentTimeMillis();
	}
	
	/**
	 * 功能描述：距离明天凌晨0点的秒数
	 *
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年6月7日
	 */
	public static int tomorrow0ExpSec() {
		return (int) (tomorrow0Exp() /1000 );
	}
	
	/**
	 * 秒之初 - 0纳秒
	 * 
	 * @param date
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDateTime startOfSecond(LocalDateTime date) {
		return date.withNano(0);
	}
	
	/**
	 * 分之初 - 0秒
	 * 
	 * @param date
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDateTime startOfMinute(LocalDateTime date) {
		return date.withSecond(0).withNano(0);
	}
	
	/**
	 * 时之初 - 0分，0秒
	 * 
	 * @param date
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDateTime startOfHour(LocalDateTime date) {
		return date.withMinute(0).withSecond(0).withNano(0);
	}
	
	/**
	 * 日之初 - 0时，0分0秒
	 * 
	 * @param date
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDateTime startOfDay(LocalDateTime date) {
		return date.withHour(0).withMinute(0).withSecond(0).withNano(0);
	}
	
	/**
	 * 月之初 - 1日，0时0分0秒
	 * 
	 * @param date
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDateTime startOfMonth(LocalDateTime date) {
		return date.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
	}

	/**
	 * 月之初 - 1日
	 *
	 * @param date
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDate startOfMonth(LocalDate date) {
		return date.withDayOfMonth(1);
	}

	/**
	 * 月之末 - 31日(时间不变)
	 *
	 * @param date
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDateTime lastOfMonth(LocalDateTime date){
		return date.with(TemporalAdjusters.lastDayOfMonth());
	}

	/**
	 * 月之末 - 31日
	 *
	 * @param date
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDate lastOfMonth(LocalDate date){
		return date.with(TemporalAdjusters.lastDayOfMonth());
	}

	/**
	 * 年之初 - 1月1日，0时0分0秒
	 * 
	 * @param date
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDateTime startOfYear(LocalDateTime date) {
		return date.withMonth(1).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
	}

	/**
	 * 年之初 - 1月1日
	 *
	 * @param date
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDate startOfYear(LocalDate date) {
		return date.withMonth(1).withDayOfMonth(1);
	}
	
	/**
	 * 周之初 - 周1，0时0分0秒
	 * 
	 * @param date
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDateTime startOfWeek(LocalDateTime date) {
		return date.with(DayOfWeek.MONDAY).withHour(0).withMinute(0).withSecond(0).withNano(0);
	}

	/**
	 * 周之初 - 周1
	 *
	 * @param date
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDate startOfWeek(LocalDate date) {
		return date.with(DayOfWeek.MONDAY);
	}
	
	/**
	 * 季之初 - 季度首日，0时0分0秒
	 * 
	 * @param date
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDateTime startOfQuarter(LocalDateTime date) {
		return date.withMonth(getStartOfQuarter(getQuarter(date.getMonthValue()))).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
	}

	/**
	 * 季之初 - 季度首日
	 *
	 * @param date
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月18日
	 */
	public static LocalDate startOfQuarter(LocalDate date) {
		return date.withMonth(getStartOfQuarter(getQuarter(date.getMonthValue()))).withDayOfMonth(1);
	}

	/**
	 * 获取日期为年的第几周
	 *
	 * @param date
	 * @return 如果周一为去年，返回0，否则返回1；相当于Mysql的WEEK(date, 1)
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static int getWeek(LocalDate date) {
		return date.get(WeekFields.of(DayOfWeek.MONDAY, 7).weekOfYear());
	}

	/**
	 * 根据年和第几周，获取周一日期
	 *
	 * @param year
	 * @param week	年的第几周
	 * @return 周的周一
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static LocalDate toLocalDateByWeek(int year, int week) {
		// 年的第1天的周一
		LocalDate date = LocalDate.of(year, 1, 1).with(DayOfWeek.MONDAY);
		// 加week周
		return week == 0 ? date : date.plusWeeks(week).with(DayOfWeek.MONDAY);
	}

	/**
	 * 获取季度
	 * <br/>
	 * 1 = 1~3，2 = 4~6，3 = 7~9，4 = 10~12
	 * 
	 * @param month	月份
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static int getQuarter(int month) {
		return month % 3 == 0 ? month / 3 : month / 3 + 1;
	}

	/**
	 * 获取季度
	 *
	 * @param month	月份
	 * @return 春夏秋冬
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static String getQuarterName(int month) {
		return DateConstant.QUARTER_MAP.get(String.valueOf(LocalDateUtil.getQuarter(month)));
	}

	/**
	 * 是否为季度的首月
	 *
	 * <br/>
	 * 1 = 1~3，2 = 4~6，3 = 7~9，4 = 10~12
	 *
	 * @param month	月份
	 * @return 1,4,7,10为true
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static boolean isQuarterMonth(int month) {
		return month % 3 == 1;
	}

	/**
	 * 获取季度的首月
	 *
	 * @param month	月份
	 * @return 1,4,7,10
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static int getQuarterMonth(int month) {
		if (month % 3 == 1) {
			// 1,4,7,10
			return month;
		} else if (month % 3 == 0) {
			// 3,6,9,12
			return month - 2;
		}else{
			// 2,5,8,11
			return month - 1;
		}
	}

	/**
	 * 获取季度开始月份
	 * 
	 * @param num	季度号，1-4
	 * @return 月
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static int getStartOfQuarter(int num) {
		if (num < 1 || num > 4) {
			throw ParamRuntimeException.ILLEGAL;
		}
		// 获取月
		return 3 * num - 2;
	}

	/**
	 * 根据年和第几季，获取季首日期
	 *
	 * @param year
	 * @param quarter	年的第几季
	 * @return 季首月的初一
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static LocalDate toLocalDateByQuarter(int year, int quarter) {
		return LocalDate.of(year, getStartOfQuarter(quarter), 1);
	}
	
	/**
	 * 加季度
	 * 
	 * @param date	日期
	 * @param num	正数+，负数-
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static LocalDate plusQuarter(LocalDate date, int num) {
		if (num == 0) {
			return date;
		}else {
			return date.plusMonths(3 * num);
		}
	}
 
	/**
	 * 加季度
	 * 
	 * @param date	日期
	 * @param num	正数+，负数-
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static LocalDateTime plusQuarter(LocalDateTime date, int num) {
		if (num == 0) {
			return date;
		}else {
			return date.plusMonths(3 * num);
		}
	}
	
	/**
	 * 减季度
	 * 
	 * @param date	日期
	 * @param num	正数-，负数+
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static LocalDate minusQuarter(LocalDate date, int num) {
		if (num == 0) {
			return date;
		}else {
			return date.minusMonths(3 * num);
		}
	}
	
	/**
	 * 减季度
	 * 
	 * @param date	日期
	 * @param num	正数-，负数+
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年12月11日
	 */
	public static LocalDateTime minusQuarter(LocalDateTime date, int num) {
		if (num == 0) {
			return date;
		}else {
			return date.minusMonths(3 * num);
		}
	}

	/**
	 * 判断是否为日期
	 * 
	 * @param date	yyyy-MM-dd
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isDate(String date) {
		if (StringUtils.isBlank(date)) {
			return false;
		}
		try {
			LocalDate.parse(date, DateConstant.DATE_FROMAT);
			return true;
		} catch (Exception e) {
			return false;
		}
	}
	
	/**
	 * 判断是否为日期时间
	 * 
	 * @param date	yyyy-MM-dd HH:mm:ss
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isDateTime(String date) {
		if (StringUtils.isBlank(date)) {
			return false;
		}
		try {
			LocalDateTime.parse(date, DateConstant.DATE_TIME_FORMAT);
			return true;
		} catch (Exception e) {
			return false;
		}
	}
	
	/**
	 * 判断是否为时间
	 * 
	 * @param date	HH:mm:ss
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isTime(String date) {
		if (StringUtils.isBlank(date)) {
			return false;
		}
		try {
			LocalTime.parse(date, DateConstant.TIME_FORMAT);
			return true;
		} catch (Exception e) {
			return false;
		}
	}
	
	/**
	 * 校验是否为日期
	 * 
	 * @param date	yyyy-MM-dd
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static void checkDate(String date) {
		if (!isDate(date)) {
			checkError(DateConstant.DATE_PATTERN, date);
		}
	}
	
	/**
	 * 校验是否为日期时间
	 * 
	 * @param date	yyyy-MM-dd HH:mm:ss
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static void checkDateTime(String date) {
		if (!isDateTime(date)) {
			checkError(DateConstant.DATE_TIME_PATTERN, date);
		}
	}
	
	/**
	 * 校验是否为时间
	 * 
	 * @param date	HH:mm:ss
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static void checkTime(String date) {
		if (!isDateTime(date)) {
			checkError(DateConstant.TIME_PATTERN, date);
		}
	}

	/**
	 * 月的天数
	 *
	 * @param date
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static int lengthOfMonth(LocalDate date){
		return date.lengthOfMonth();
	}

	/**
	 * 年的天数
	 *
	 * @param date
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static int lengthOfYear(LocalDate date){
		return date.lengthOfYear();
	}

	/**
	 * 在区间：start <= date < end
	 *
	 * @param date
	 * @param start	包含
	 * @param end	不包含
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isRange(LocalDate date, LocalDate start, LocalDate end){
		return !date.isAfter(start) && date.isBefore(end);
	}

	/**
	 * 在区间：start <= date < end
	 *
	 * @param date
	 * @param start	包含
	 * @param end	不包含
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isRange(LocalDateTime date, LocalDateTime start, LocalDateTime end){
		return !date.isAfter(start) && date.isBefore(end);
	}

	/**
	 * 在区间：start <= date < end
	 *
	 * @param date
	 * @param start	包含
	 * @param end	不包含
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isRange(LocalDateTime date, LocalDate start, LocalDate end){
		return !date.isAfter(start.atStartOfDay()) && date.isBefore(end.atStartOfDay());
	}

	/**
	 * 在区间：start <= date < end
	 *
	 * @param date
	 * @param start	包含
	 * @param end	不包含
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isRange(LocalDate date, LocalDateTime start, LocalDateTime end){
		return !date.isAfter(start.toLocalDate()) && date.isBefore(end.toLocalDate());
	}

	/**
	 * 小于等于：date <= other
	 *
	 * @param date
	 * @param other
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isLe(LocalDate date, LocalDate other){
		return date.isBefore(other) || date.isEqual(other);
	}

	/**
	 * 小于等于：date <= other
	 *
	 * @param date
	 * @param other
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isLe(LocalDateTime date, LocalDateTime other){
		return date.isBefore(other) || date.isEqual(other);
	}

	/**
	 * 大于等于：date >= other
	 *
	 * @param date
	 * @param other
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isGe(LocalDate date, LocalDate other){
		return date.isAfter(other) || date.isEqual(other);
	}

	/**
	 * 大于等于：date >= other
	 *
	 * @param date
	 * @param other
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	public static boolean isGe(LocalDateTime date, LocalDateTime other){
		return date.isAfter(other) || date.isEqual(other);
	}

	/**
	 * 取最大值
	 *
	 * @param a
	 * @param b
	 * @return max
	 *
	 * @author 延晓磊
	 * @since 2022年11月04日
	 */
	public static LocalDateTime max(LocalDateTime a, LocalDateTime b){
		return a.isAfter(b) ? a : b;
	}

	/**
	 * 是否为工作日：周1 ~ 周五
	 *
	 * @param date
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年09月20日
	 */
	public static boolean isWorkDay(LocalDate date){
		return !(date.getDayOfWeek().equals(DayOfWeek.SATURDAY) || date.getDayOfWeek().equals(DayOfWeek.SUNDAY));
	}

	/**
	 * 是否为工作时间：8时 ~ 18时
	 *
	 * @param time
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年09月20日
	 */
	public static boolean isWorkTime(LocalTime time){
		return time.getHour() >= 8 && time.getHour() < 18;
	}


	/**
	 * 时间校验报错
	 * 
	 * @param format
	 * @param date
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年4月3日
	 */
	private static void checkError(String format, String date) {
		throw new ParamRuntimeException(StringUtil.logFormat("时间格式错误：要求【{}】，参数【{}】", format, date));
	}

	/**
	 * 毫秒转纳秒
	 *
	 * @param ms
	 * @return
	 */
	public static long toNs(long ms){
		if (NumberUtil.length(ms) == 13) {
			ms = Long.parseLong(ms + RandomStringUtils.randomNumeric(6));
		}
		return ms;
	}

	/**
	 * 纳秒转毫秒
	 *
	 * @param ns
	 * @return
	 */
	public static long toMs(long ns){
		if (NumberUtil.length(ns) == 19) {
			ns = Long.parseLong(String.valueOf(ns).substring(0,13));
		}
		return ns;
	}
}
